﻿
module Octopus.OctopusLib.Functions

open System
open System.IO


let sendMessage recipient text =
  // who cares if the message is not sendable? I use them to send exceptions... :D
  Async.Start (async { Utility.execute_remote_Octopus_function [| "sendMessage"; recipient; text |] })

let getMessages recipient =
  let sarr = Utility.execute_remote_Octopus_function_withArrayReturn [| "getMessages"; recipient |]
  sarr |> Array.map (fun x -> UserMessage.Deserialize(x))


let getAllVm () =
  let sarr = Utility.execute_remote_Octopus_function_withArrayReturn [| "getAllVm" |]
  sarr |> Array.map (fun x -> VirtualMachine.Deserialize(x))

let getAllVm_byOwner ownerName =
  let sarr = Utility.execute_remote_Octopus_function_withArrayReturn [| "getVmByOwner"; ownerName |]
  sarr |> Array.map (fun x -> VirtualMachine.Deserialize(x))


let createNewVm userName (templateId:int) (numOfVm:int) (cpuCount:int) (memorySizeMb:uint64) hostName password =
  let pars = [| "createNewVm"; userName; string templateId; string numOfVm; string cpuCount; string memorySizeMb; hostName; password |]
  let guid = Utility.execute_remote_Octopus_function_withReturn pars
  new System.Guid(guid)


let setVmCheckpoint userName (vmGuids:System.Guid array) =
  let pars = [| yield "setVmCheckpoint"; yield string vmGuids.Length; for g in vmGuids -> g.ToString() |]
  Utility.execute_remote_Octopus_function pars

let commitVmCheckpoint userName (vmGuids:System.Guid array) =
  let pars = [| yield "commitVmCheckpoint"; yield string vmGuids.Length; for g in vmGuids -> g.ToString() |]
  Utility.execute_remote_Octopus_function pars

let discardVmCheckpoint userName (vmGuids:System.Guid array) =
  let pars = [| yield "discardVmCheckpoint"; yield string vmGuids.Length; for g in vmGuids -> g.ToString() |]
  Utility.execute_remote_Octopus_function pars

let stampVm userName (vmGuid:System.Guid) =
  Utility.execute_remote_Octopus_function [| "stampVm"; vmGuid.ToString() |]


let deleteVm (userName:string) (vmGuid:System.Guid) =
  Utility.execute_remote_Octopus_function [| "deleteVm"; vmGuid.ToString() |]

let deleteVmAsync userName vmGuid =
  Async.Start (async { try deleteVm userName vmGuid with e -> sendMessage userName e.Message })

let startVm (userName:string) (vmGuid:System.Guid) =
  Utility.execute_remote_Octopus_function [| "startVm"; vmGuid.ToString() |]

let startVmAsync userName vmGuid =
  Async.Start (async { try startVm userName vmGuid with e -> sendMessage userName e.Message })

let turnoffVm (userName:string) (vmGuid:System.Guid) =
  Utility.execute_remote_Octopus_function [| "turnoffVm"; vmGuid.ToString() |]

let turnoffVmAsync userName vmGuid =
  Async.Start (async { try turnoffVm userName vmGuid with e -> sendMessage userName e.Message })

let suspendVm (userName:string) (vmGuid:System.Guid) =
  Utility.execute_remote_Octopus_function [| "suspendVm"; vmGuid.ToString() |]

let suspendVmAsync userName vmGuid =
  Async.Start (async { try suspendVm userName vmGuid with e -> sendMessage userName e.Message })


//-----------------------

let getVmByGuid (octopusGuid:System.Guid) =
  let ret = Utility.execute_remote_Octopus_function_withReturn [| "getVmByGuid"; string octopusGuid |]
  VirtualMachineData.Deserialize(ret)

let getActiveVmByGuid (octopusGuid:System.Guid) =
  let ret = Utility.execute_remote_Octopus_function_withReturn [| "getActiveVmByGuid"; string octopusGuid |]
  VirtualMachine.Deserialize(ret)

let getVmStateByGuid (octopusGuid:System.Guid) =
  Utility.execute_remote_Octopus_function_withReturn [| "getVmStateByGuid"; string octopusGuid |]

let getVmIPv4AddressByGuid (octopusGuid:System.Guid) =
  Utility.execute_remote_Octopus_function_withReturn [| "getVmIPv4AddressByGuid"; string octopusGuid |]

let getVmRDPAddressByGuid (octopusGuid:System.Guid) =
  Utility.execute_remote_Octopus_function_withReturn [| "getVmRDPAddressByGuid"; string octopusGuid |]


//--------------------------------

let getAllVmTemplates () =
  Utility.execute_remote_Octopus_function_withArrayReturn [| "getAllVmTemplates" |]
    |> Array.map (fun x -> VMTemplate.Deserialize(x))

let getVmTemplateById (templateId:int) =
  let ret = Utility.execute_remote_Octopus_function_withReturn [| "getVmTemplatesById"; string templateId |]
  VMTemplate.Deserialize(ret)

let createNewVmTemplate name description (cpuCountMin:int) (cpuCountMax:int) (memorySizeMin:int) (memorySizeMax:int) nicSwitchName vhdPath (isHpcNode:bool) hpcScheduler (parentTemplate:VMTemplate option) =
  match parentTemplate with
  | None -> Utility.execute_remote_Octopus_function [| "createNewVmTemplate"; "-1"; name; description; string cpuCountMin; string cpuCountMax; string memorySizeMin; string memorySizeMax; nicSwitchName; vhdPath; string isHpcNode; hpcScheduler |]
  | Some(t) -> Utility.execute_remote_Octopus_function [| "createNewVmTemplate"; (string t.ID); name; description; string cpuCountMin; string cpuCountMax; string memorySizeMin; string memorySizeMax; nicSwitchName; vhdPath; string isHpcNode; hpcScheduler |]

let createNewVmTemplate2 (t:VMTemplate) =
  createNewVmTemplate t.Name t.Description (int t.CpuCoresMin) (int t.CpuCoresMax) t.MemorySizeMbMin t.MemorySizeMbMax t.NicSwitchName t.VhdPath t.IsHpcNode t.HpcScheduler
  

//--------------------------------

(*
let migrateVmAsync (userName:string) (vmGuid:System.Guid) (*destServer:string*) =
  let sendMsg recipient text =
    Utility.execute_remote_Octopus_function [| "sendMessage"; recipient; text |]

  let doMigrate() =
    try
      let vm = getVmByGuid vmGuid
      let hosts = 
        let sarr = Utility.execute_remote_Octopus_function_withArrayReturn [| "tmpGetHyperVNodes" |]
        sarr |> Array.map (fun x -> HyperVNode.Deserialize(x))
      
      // select one host, different from the source one, which have at least
      // 2.5 Gb of free ram
      let mutable destServer = vm.CurrentServer
      while vm.CurrentServer.ToUpper() = destServer.ToUpper() do
        let i = (new System.Random()).Next(0, hosts.Length)
        if hosts.[i].LastMemoryFree > 2684354560UL then
          destServer <- hosts.[i].HostName

      let state = getVmStateByGuid vmGuid
      if state = "Running" then
        Utility.execute_remote_Octopus_function [| "migrateRunningVm"; vmGuid.ToString(); destServer |]
      else if state = "Stopped" || state = "Suspended" then
        Utility.execute_remote_Octopus_function [| "migrateVm"; vmGuid.ToString(); destServer |]
      else
        sendMsg userName ("Unable to migrate a VM in " + state + " state")
        failwithf "Unable to migrate a VM in %s state" state
    with e -> sendMsg userName e.Message      
  
  Async.Start (async { doMigrate() })
*)


// --------------------------
// For debug purpose only

let tmpGetHyperVNodes () =
  let sarr = Utility.execute_remote_Octopus_function_withArrayReturn [| "tmpGetHyperVNodes" |]
  sarr |> Array.map (fun x -> HyperVNode.Deserialize(x))



// ----------------------------------------------------------
// Thumbnails functions
// - to little improve the performance, it calls directly the HyperF library


let checkThumbAvailable (vm:VirtualMachine) (hfvm:HyperF.VirtualMachineSummary) =
  if vm.OctopusState = OctopusVmState.Migrating then false
  else if hfvm.HealthState = HyperF.VMHealthState.CriticalFailure then false
  else if hfvm.HealthState = HyperF.VMHealthState.MajorFailure then false
  else true


let getThumbnail (octopusGuid:System.Guid) width height =
  let vm = getActiveVmByGuid octopusGuid
  let hfvm = HyperF.HyperF.get_VMSummary (Some vm.CurrentServer) vm.HyperVGuid
  let img =
    if checkThumbAvailable vm hfvm then
      HyperF.HyperF.get_VMThumbnailImage hfvm width height
    else 
      null
      
  if img = null then null
  else
    let bmp = new System.Drawing.Bitmap(int width, int height)
    let rect = Drawing.Rectangle(0, 0, bmp.Width, bmp.Height)
    let data = bmp.LockBits(rect, Drawing.Imaging.ImageLockMode.WriteOnly, Drawing.Imaging.PixelFormat.Format16bppRgb565)
    System.Runtime.InteropServices.Marshal.Copy(img, 0, data.Scan0, img.Length)
    bmp.UnlockBits(data)
    bmp

let getThumbnail_or_blank octopusGuid (width:uint32) (height:uint32) =
  let blankThumb (x) =
      let bmp = new System.Drawing.Bitmap(int width, int height)
      use g = System.Drawing.Graphics.FromImage(bmp)
      g.Clear(x)
      bmp

  try
    let vm = getActiveVmByGuid octopusGuid    
    let hfvm = HyperF.HyperF.get_VMSummary (Some vm.CurrentServer) vm.HyperVGuid

    if checkThumbAvailable vm hfvm then
        let (width,height) =
          let screen = HyperF.HyperF.get_VMEnabledVideo hfvm
          if width > screen.ResolutionH || height > screen.ResolutionV then (screen.ResolutionH, screen.ResolutionV)
          else (width,height)
                         
        let bmp = getThumbnail octopusGuid width height
        if bmp <> null then bmp
        else blankThumb(System.Drawing.Color.LightGray)
    else
        blankThumb(System.Drawing.Color.LightGray)
  with _ ->
    blankThumb(System.Drawing.Color.LightGray)
